
package com.ray.tool;

import java.io.Closeable;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * LiaoYuXing-Ray 多年的心血工具类
 * 完全仅依赖于JDK8
 *
 * @author LiaoYuXing-Ray
 * @version 1.0
 * @createDate 2023/8/31 16:43
 **/
@SuppressWarnings("unused")
public class RayTools {

    /**
     * 工具类中的方法都是静态方法访问的，因此将构造器私有不允许创建对象
     *
     * @author LiaoYuXing-Ray 2023/9/7 10:28
     */
    private RayTools() {
        throw new AssertionError();
    }

    /**
     * 尝试关闭传入对象 - 【常用】
     *
     * @param object 实现 {@link Closeable} 的对象
     * @author LiaoYuXing-Ray 2023/9/14 19:12
     **/
    public static void closeObject(Closeable object) {
        if (object != null) {
            try {
                object.close();
            } catch (Exception ex) {
                System.err.println("关闭失败！" + ex.getMessage());
            }
        }
    }

    /**
     * 尝试关闭多个传入对象 - 【常用】
     *
     * @param objects 实现 {@link Closeable} 的对象
     * @author LiaoYuXing-Ray 2023/9/14 19:12
     **/
    public static void closeObject(Closeable... objects) {
        if (objects != null && objects.length >= 1) {
            for (Closeable obj : objects) {
                closeObject(obj);
            }
        }
    }

    /**
     * 加载指定路径下的jar包中的类
     *
     * @param jarFilePath jar文件路径
     * @param className   类名
     * @return java.lang.Object
     * @author LiaoYuXing-Ray 2023/10/7 14:00
     **/
    public static Class<?> loadClass(String jarFilePath, String className) throws Exception {
        URL url = (new File(jarFilePath)).toURI().toURL();
        // 通过反射获取URLClassLoader类的addURL方法
        Method add = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        // 检查addURL方法是否可访问
        boolean accessible = add.isAccessible();
        try {
            // 如果addURL方法不可访问，则将其设置为可访问
            if (!accessible) {
                add.setAccessible(true);
            }
            // 通过反射获取系统类加载器
            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            // 调用addURL方法，将目标服务的URL添加到系统类加载器中
            add.invoke(classLoader, url);
        } finally {
            // 将addURL方法的可访问性还原
            add.setAccessible(accessible);
        }
        // 通过反射获取指定的类，并返回该类的实例
        return Class.forName(className);
    }

    /**
     * 判断给定的字符序列是否为空白。
     *
     * @param str 要检查的字符序列
     * @return 如果字符序列为null、空字符串或只包含空白字符，则返回true；否则返回false。
     * @author LiaoYuXing-Ray 2023/9/7 10:34
     */
    public static boolean isBlank(CharSequence str) {
        int length;
        if (str == null) {
            return true;
        }
        // 如果循环结束后没有找到非空白字符，或者str为null或长度为0，则函数返回true，表示字符序列是空白的。
        if ((length = str.length()) != 0) {
            for (int i = 0; i < length; ++i) {
                if (!isBlankChar(str.charAt(i))) {
                    return false;
                }
            }
        }
        return true;
    }


    /**
     * 判断给定的整数是否表示一个空白字符。
     * Character.isWhitespace(c)：判断整数是否表示一个空白字符（如空格、制表符等）。
     * Character.isSpaceChar(c)：判断整数是否表示一个Unicode标准定义的空格字符。
     * c == 65279：判断整数是否等于常量值65279，该常量表示一个特殊的空白字符。
     * c == 8234：判断整数是否等于常量值8234，该常量也表示一个特殊的空白字符。
     * 如果满足上述任一条件，则函数返回true，表示给定的整数是一个空白字符；否则返回false。
     *
     * @param c 要检查的整数
     * @return 如果整数表示一个空白字符，则返回true；否则返回false
     * @author LiaoYuXing-Ray 2023/9/7 10:36
     */
    public static boolean isBlankChar(int c) {
        return Character.isWhitespace(c) || Character.isSpaceChar(c) || c == 65279 || c == 8234;
    }


    /**
     * 将长整型数转换为字节数组
     *
     * @param len 长整型数
     * @return byte[]
     * @author LiaoYuXing-Ray 2023/9/7 10:17
     **/
    public static byte[] intToByteArray(long len) {
        // 创建一个新的字节数组，长度为4
        return new byte[]{
                // 将长整型数的最高8位转换为字节，并存入数组的第一个元素
                (byte) ((int) (len >> 24)),
                // 将长整型数的次高8位转换为字节，并存入数组的第二个元素
                (byte) ((int) (len >> 16)),
                // 将长整型数的次低8位转换为字节，并存入数组的第三个元素
                (byte) ((int) (len >> 8)),
                // 将长整型数的最低8位转换为字节，并存入数组的第四个元素
                (byte) ((int) len)
        };
    }

    /**
     * 将字节数组转换为整型数
     *
     * @param arr 字节数组
     * @return int 整型数
     * @author LiaoYuXing-Ray 2023/9/7 10:23
     **/
    public static int byteArrayToInt(byte[] arr) {
        int result = 0;
        int len = arr.length;

        /*
            将元素与255进行位与运算，这样可以去除高位的符号位，只保留低8位的数据。
            然后将结果左移(3 - i) * 8位，这是因为一个字节有8位，从低位到高位分别是第1、2、3、4、5、6、7、8位，所以需要将数据移动到正确的位置上。
         */
        for (int i = 0; i < len; ++i) {
            result += (arr[i] & 255) << (3 - i) * 8;
        }

        return result;
    }

    /**
     * IP地址正则表达式 2023/10/21 13:50
     */
    private final static String patternIp = "\\d+\\.\\d+\\.\\d+\\.\\d+";
    /**
     * 定义一个正则表达式模式，用于匹配IP地址
     * 利用好其预编译功能，可以有效加快正则匹配速度
     * 2023/10/21 13:50
     */
    private final static Pattern ip = Pattern.compile(patternIp);

    /**
     * 从给定字符串文本中提取IP地址并输出
     * 输出文本中所有：提取到的IP地址，如果未找到匹配的IP地址则不输出
     *
     * @param src 可能包含IP地址的字符串文本，格式为x.x.x.x
     * @author LiaoYuXing-Ray 2023/8/31 17:35
     */
    public static void getIp(String src) {
        Matcher matcher = ip.matcher(src);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }

    /**
     * 将byte数组转为UTF-8字符串
     *
     * @param byteArray byte数组
     * @return UTF-8字符串
     * @author LiaoYuXing-Ray 2023/9/7 9:51
     */
    public static String byteArrayToUtf8String(byte[] byteArray) {
        return new String(byteArray, StandardCharsets.UTF_8);
    }


    /**
     * 获取当前JVM进程的PID ProcessId
     *
     * @return 当前JVM进程的PID
     * @author LiaoYuXing-Ray 2023/9/15 16:57
     */
    public static int getCurrentPID() {
        String jvmName = ManagementFactory.getRuntimeMXBean().getName();
        int index = jvmName.indexOf('@');
        String pid = jvmName.substring(0, index);
        return Integer.parseInt(pid);
    }


    /**
     * 对象转集合
     *
     * @param obj   待转对象
     * @param clazz 待转集合
     * @return java.util.List<T> 转后的集合
     * @author LiaoYuXing-Ray 2023/9/18 16:50
     **/
    public static <T> List<T> objectToList(Object obj, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        if (obj instanceof ArrayList<?>) {
            for (Object o : (List<?>) obj) {
                list.add(clazz.cast(o));
            }
            return list;
        }
        return null;
    }


    /**
     * 获取逗号分隔并加单引号的编码字符串，适用于字符串集合
     * 如果是空集合，返回""
     *
     * @param strList 字符串集合
     * @return java.lang.String 逗号分隔并加单引号的编码字符串
     * @author LiaoYuXing-Ray 2023/9/18 16:57
     **/
    public static String getSingleQuotationMarksStr(List<String> strList) {
        if (null != strList && strList.size() > 0) {
            strList.replaceAll(s -> "'" + s + "'");
            return String.join(",", strList);
        }
        return "";
    }

    /**
     * 获取逗号分隔并加单引号的编码字符串，适用于逗号分隔的编码字符串
     * 不支持空串，调用者自行判断空串
     *
     * @param str 逗号分隔的编码字符串
     * @return java.lang.String 逗号分隔并加单引号的编码字符串
     * @author LiaoYuXing-Ray 2023/9/18 16:57
     **/
    public static String getSingleQuotationMarksStr(String str) {
        if (isBlank(str)) {
            throw new RuntimeException("不支持空串");
        }
        String[] strArray = str.split(",");
        if (strArray.length > 0) {
            for (int i = 0; i < strArray.length; i++) {
                strArray[i] = String.format("'%s'", strArray[i]);
            }
        }
        return String.join(",", strArray);
    }

    /**
     * 获取逗号分隔并加双引号的编码字符串，适用于逗号分隔的编码字符串
     * 不支持空串，调用者自行判断空串
     *
     * @param str 逗号分隔的编码字符串
     * @return java.lang.String 逗号分隔并加双引号的编码字符串
     * @author LiaoYuXing-Ray 2023/9/18 16:57
     **/
    public static String getDoubleQuotationMarksStr(String str) {
        if (isBlank(str)) {
            throw new RuntimeException("不支持空串");
        }
        String[] strArray = str.split(",");
        if (strArray.length > 0) {
            for (int i = 0; i < strArray.length; i++) {
                strArray[i] = String.format("\"%s\"", strArray[i]);
            }
        }
        return String.join(",", strArray);
    }

    /**
     * 获取当前执行的方法名
     *
     * @return java.lang.String
     * @author LiaoYuXing-Ray 2023/9/28 10:11
     **/
    public static String getMethodName() {
        return Thread.currentThread().getStackTrace()[2].getMethodName();
    }

    /**
     * 获取当前执行的类名
     *
     * @return java.lang.String
     * @author LiaoYuXing-Ray 2023/9/28 10:11
     **/
    public static String getClassName() {
        return Thread.currentThread().getStackTrace()[2].getClassName();
    }


    /**
     * 判断给定的字符序列是否为非空白。
     *
     * @param str 要检查的字符序列
     * @return 返回true为非空白字符串；否则返回false。
     * @author LiaoYuXing-Ray 2023/9/7 10:34
     */
    public static boolean isNotBlank(CharSequence str) {
        return !isBlank(str);
    }


    public static boolean isEmpty(CharSequence str) {
        return !hasLength(str);
    }

    public static boolean hasLength(CharSequence str) {
        return str != null && str.length() > 0;
    }

    /**
     * 获取字符串中的整型值
     *
     * @param str 包含数字的字符串
     * @return int 字符串中包含的整型值
     * @throws Exception 异常信息
     * @author LiaoYuXing 2023/8/21 11:23
     **/
    public static int getNumbersInString(String str) throws Exception {
        if (isNotBlank(str)) {
            StringBuilder sb = new StringBuilder();
            Pattern pattern = Pattern.compile("\\d+");
            Matcher matcher = pattern.matcher(str);
            while (matcher.find()) {
                sb.append(matcher.group());
            }
            return Integer.parseInt(sb.toString());
        } else {
            throw new Exception("传入空字符串");
        }
    }

    /**
     * 集合初始化时，指定集合初始值大小。防止扩容影响性能
     * initialCapacity = (需要存储的元素个数 / 负载因子) + 1。
     * 注意负载因子（即 loader factor）默认为 0.75，如果暂时无法确定初始值大小，请设置为 16（即默认值）
     *
     * @param elementCount 需要存储的元素个数
     * @return int 初始值大小
     * @author LiaoYuXing-Ray 2023/10/20 16:15
     **/
    public static int geInitialCapacity(Integer elementCount) {
        if (elementCount == null) {
            // 默认值
            return 16;
        }
        return (int) ((elementCount / 0.75) + 1);
    }

    /**
     * 浮点型转化为bit，字符串输出，会自动补零
     *
     * @param value 浮点数
     * @return java.lang.String
     * @author LiaoYuXing-Ray 2023/10/25 11:02
     **/
    public static String floatToBinary(float value) {
        int intBits = Float.floatToIntBits(value);
        return String.format("%32s", Integer.toBinaryString(intBits)).replace(' ', '0');
    }
}
